<?php


namespace App\service;

class TokenBucket
{
    private $_lastTime;     //记录每次请求的时间
    private $_burst = 10;    // 桶的容量
    private $_rate = 1;     // 令牌放入的速度(访问次数除以时间=速率)
    private $_tokens = 10;       // 当前令牌的数量
    private $_redis;        //redis对象

    public function __construct()
    {
        $this->_redis = new \Redis();
        $this->_redis->connect('127.0.0.1', 6379);
        $this->_lastTime = time(); //需要记录每次访问的时间
    }

    //要访问的接口
    public function getApi()
    {
        $res = (new TokenBucket())->tokenBucket();
        $data = [
            'msg' => '获取成功',
            'code' => 200
        ];
        if (!$res) {
            $data['msg'] = '请稍后重试';
            $data['code'] = 400;
        }
        return $data;
    }

    public function tokenBucket()
    {
        $now = time();
        $redisKey = 'tokenBucket_api';
        //如果该key存在 获取上一次访问时间和当前令牌数量
        if (!empty($time = $this->_redis->get($redisKey))) {
            $this->_lastTime = $time;
        }

        if (!empty($tokens = $this->_redis->get('tokens'))) {
            $this->_tokens = $tokens;
        }
        //当前时间减去上次访问时间，得到时间间隔
        $s = $now - $this->_lastTime;
        //生成的令牌 =(时间间隔)* 放入令牌的速率
        $addTokens = $s * $this->_rate;
        //--------------------放入令牌------------------------------------------
        //当前令牌数= 之前的桶内令牌数量+放入的令牌数量(不能超过桶的总量所以用min 取一个比总桶量小的值)
        $this->_tokens = min($this->_burst, $this->_tokens + $addTokens);
        //-------------------领取令牌----------------------------------------
        $this->_lastTime = $now;
        //令牌-1
        $this->_tokens -= 1;
        //记录领取时间和key
        $this->_redis->set($redisKey, $now);

//        echo "令牌量:" . $this->_tokens;

        // 若令牌为空,则拒绝访问
        if ($this->_tokens < 1) {
            echo "拿不到令牌" . PHP_EOL;
            return false;
        }
        //领取完更新当前令牌数
        $this->_redis->set('tokens', $this->_tokens);
        return true;
    }
}
